import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class BezierPainter extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _BezierPainterStatue();
  }
}

class _BezierPainterStatue extends State<BezierPainter> {
  List<Offset> _pos = <Offset>[];
  int _selectPos;
  var _width;
  var _height;
  OverlayEntry entry;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      ThemeData themeData = Theme.of(context);
      entry = OverlayEntry(builder: (context) {
        return Positioned(
          top: 30,
          left: 10,
          width: 50,
          height: 50,
          child: Material(
            type: MaterialType.transparency,
            child: InkWell(
              onTap: () {
                setState(() {
                  _pos.clear();
                });
              },
              child: Card(
                color: themeData.accentColor,
                child: Icon(Icons.clear),
              ),
            ),
          ),
        );
      });
      Overlay.of(context).insert(entry);
    });
  }

  Offset _addOffset(Offset offset) {
    return Offset(offset.dx - _width / 2, offset.dy - _height / 2);
  }

  int _getSelectOffsetPos(Offset offset) {
    Offset set = _addOffset(offset);
    int index = 0;
    for (Offset s in _pos) {
      if ((s - set).distance < 10) {
        break;
      }
      index++;
    }
    return index;
  }

  @override
  Widget build(BuildContext context) {
    _width = MediaQuery.of(context).size.width;
    _height = MediaQuery.of(context).size.height;
    // TODO: implement build
    return RepaintBoundary(
      child: Material(
        child: GestureDetector(
          onPanDown: (detail) {
            print("1");
            setState(() {
              if (_pos.length < 3) {
                _pos.add(_addOffset(detail.localPosition));
              }
              _selectPos = _getSelectOffsetPos(detail.localPosition);
            });
          },
          onPanEnd: (detail) {
            print("2");
            setState(() {
              _selectPos = null;
            });
          },
          onPanUpdate: (detail) {
            if (_selectPos != null) {
              setState(() {
                _pos[_selectPos] = _addOffset(detail.localPosition);
              });
            }
          },
          child: CustomPaint(
            painter: BezierPainterChild(pos: _pos),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    entry.remove();
  }
}

class BezierPainterChild extends CustomPainter {
  final List<Offset> pos;
  final int selectPos;

  Paint _gridPaint;
  Path _gridPath;

  Paint _mainPaint;
  Path _mainPath;

  Offset p0 = Offset(0, 0);
  Offset p1 = Offset(100, 100);
  Offset p2 = Offset(120, -60);

  Paint _helpPaint;

  BezierPainterChild({@required this.pos, this.selectPos}) {
    _gridPaint = Paint()..style = PaintingStyle.stroke;
    _gridPath = Path();

    _mainPaint = Paint()
      ..style = PaintingStyle.stroke
      ..color = Colors.deepOrange
      ..strokeWidth = 2;
    _mainPath = Path();

    _helpPaint = Paint()
      ..color = Colors.green
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
  }

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint

    print(pos);

    if (pos.length == 3) {
      canvas.drawColor(Colors.white, BlendMode.color);
      canvas.translate(size.width / 2, size.height / 2);
      _drawGrid(canvas, size);
      _drawAxis(canvas, size);

      canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);

      _mainPath.moveTo(pos[0].dx, pos[0].dy);
      _mainPath.quadraticBezierTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy);
      canvas.drawPath(_mainPath, _mainPaint);

      _drawHelp(canvas);
    } else {
      canvas.drawColor(Colors.white, BlendMode.color);
      canvas.translate(size.width / 2, size.height / 2);

      _drawGrid(canvas, size);
      _drawAxis(canvas, size);

      canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

  void _drawGrid(Canvas canvas, Size size) {
    _gridPaint
      ..color = Colors.grey
      ..strokeWidth = 0.5;
    _gridPath = _buildGridPath(_gridPath, size);
    canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint);

    canvas.save();
    canvas.scale(1, -1); //沿x轴镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();

    canvas.save();
    canvas.scale(-1, 1); //沿y轴镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();

    canvas.save();
    canvas.scale(-1, -1); //沿原点镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();
  }

  void _drawAxis(Canvas canvas, Size size) {
    canvas.drawPoints(
        PointMode.lines,
        [
          Offset(-size.width / 2, 0),
          Offset(size.width / 2, 0),
          Offset(0, -size.height / 2),
          Offset(0, size.height / 2),
          Offset(0, size.height / 2),
          Offset(0 - 7.0, size.height / 2 - 10),
          Offset(0, size.height / 2),
          Offset(0 + 7.0, size.height / 2 - 10),
          Offset(size.width / 2, 0),
          Offset(size.width / 2 - 10, 7),
          Offset(size.width / 2, 0),
          Offset(size.width / 2 - 10, -7),
        ],
        _gridPaint
          ..color = Colors.blue
          ..strokeWidth = 1.5);
  }

  Path _buildGridPath(Path path, Size size, {step = 20.0}) {
    for (int i = 0; i < size.height / 2 / step; i++) {
      path.moveTo(0, step * i);
      path.relativeLineTo(size.width / 2, 0);
    }
    for (int i = 0; i < size.width / 2 / step; i++) {
      path.moveTo(step * i, 0);
      path.relativeLineTo(
        0,
        size.height / 2,
      );
    }
    return path;
  }

  void _drawHelp(Canvas canvas) {
    canvas.drawPoints(PointMode.points, [pos[0], pos[1], pos[1], pos[2]],
        _helpPaint..strokeWidth = 8);
    canvas.drawPoints(PointMode.lines, [pos[0], pos[1], pos[1], pos[2]],
        _helpPaint..strokeWidth = 1);
  }
}
